﻿using System;
using System.IO;
using System.Xml.Serialization;
using Ogdi.Data.DataLoader.Csv;
using Ogdi.Data.DataLoader.DbaseKml;

namespace Ogdi.Data.DataLoader
{
    public enum SourceDataType
    {
        DbfAndKml,
        Csv
    }

    public enum DataLoadingTarget
    {
        Console,
        Tables
    }

    //What to do if data set is already in storage (only for TableDataLoader and TableEntityProcessor)
    public enum TableOverwriteMode
    {
        Create, //wipe and reload
        Add, //add to existing data set (must exist, must have identical metadata except for last updated date which should be newer and be updated), raise exception when inserting entity with the same rawID
        Update //update or add data to existing data set (conditions  should be the same as in add, update entity if exist, add if not)
    }

    public static class DataLoaderFactory
    {
        public static IDataLoader CreateDataLoader(SourceDataType type, DataLoadingTarget target, string fileSetName, TableOverwriteMode overwriteMode, bool sourceOrder)
        {
            string dir = Directory.GetCurrentDirectory();
            string file = fileSetName + DataLoaderConstants.FileExtConfig;

            using (var stream = File.Open(Path.Combine(dir, file), FileMode.Open, FileAccess.Read))
                switch (type)
                {
                    case SourceDataType.DbfAndKml:
                        return CreateDbfAndKmlLoader(fileSetName, target, stream, sourceOrder, overwriteMode);

                    case SourceDataType.Csv:
                        return CreateCsvLoader(fileSetName, target, stream, sourceOrder, overwriteMode);
                }
            throw new NotSupportedException(type.ToString());
        }

        private static DataLoader CreateCsvLoader(string fileSetName, DataLoadingTarget target, FileStream stream, bool sourceOrder, TableOverwriteMode overwriteMode)
        {
            switch (target)
            {
                case DataLoadingTarget.Console:
                    return CreateCsvConsoleLoader(stream, fileSetName, sourceOrder);
                case DataLoadingTarget.Tables:
                    return GetCsvTablesLoader(stream, fileSetName, sourceOrder, overwriteMode);
            }
            throw new NotSupportedException(target.ToString());
        }

        private static DataLoader GetCsvTablesLoader(FileStream stream, string fileSetName, bool sourceOrder, TableOverwriteMode overwriteMode)
        {
            var serializer = new XmlSerializer(typeof(CsvToTablesDataLoaderParams));
            var p = (CsvToTablesDataLoaderParams)serializer.Deserialize(stream);
            p.TableMetadataEntity.EntityKind = p.TableMetadataEntity.EntitySet + "Item";
            if (p.ProcessorParams != null)
            {
                p.ProcessorParams.EntityKind = p.TableMetadataEntity.EntityKind;
                p.ProcessorParams.EntitySet = p.TableMetadataEntity.EntitySet;
            }

            if (sourceOrder && p.ProcessorParams != null && !String.IsNullOrEmpty(p.ProcessorParams.PartitionKeyPropertyName)
                    && p.ProcessorParams.PartitionKeyPropertyName.ToLower() != DataLoaderConstants.ValueUniqueAutoGen.ToLower())
                throw new ApplicationException("PartitionKey must be empty or equal to 'New.Guid' in .cfg when use sourceOrder");

            if (overwriteMode == TableOverwriteMode.Update && p.ProcessorParams != null
                && (String.IsNullOrEmpty(p.ProcessorParams.PartitionKeyPropertyName)
                    || p.ProcessorParams.PartitionKeyPropertyName.ToLower() == DataLoaderConstants.ValueUniqueAutoGen.ToLower()
                    || String.IsNullOrEmpty(p.ProcessorParams.PartitionKeyPropertyName)
                    || p.ProcessorParams.PartitionKeyPropertyName.ToLower() == DataLoaderConstants.ValueUniqueAutoGen.ToLower()))
                throw new ApplicationException("PartitionKey and RowKey must be provided in .cfg for /mode=update");

            EntityProducer producer = null;

            if (!p.TableMetadataEntity.IsEmpty)
                producer = new CsvEntityProducer(
                    fileSetName,
                    p.TableMetadataEntity.EntitySet,
                    p.TableMetadataEntity.EntityKind,
                    p.ProducerParams,
                    sourceOrder);
            var processor = new TableEntityProcessor(p.ProcessorParams, overwriteMode);
            return new TableDataLoader(p, producer, processor, overwriteMode);
        }

        private static DataLoader CreateCsvConsoleLoader(FileStream stream, string fileSetName, bool sourceOrder)
        {
            var serializer = new XmlSerializer(typeof(DataLoaderParams));
            var p = (DataLoaderParams)serializer.Deserialize(stream);
            p.TableMetadataEntity.EntityKind = p.TableMetadataEntity.EntitySet + "Item";
            EntityProducer producer = null;
            if (!p.TableMetadataEntity.IsEmpty)
                producer = new CsvEntityProducer(
                    fileSetName,
                    p.TableMetadataEntity.EntitySet,
                    p.TableMetadataEntity.EntityKind,
                    p.ProducerParams,
                    sourceOrder);
            var processor = new CoutEntityProcessor();
            var loader = new DataLoader(p, producer, processor);
            return loader;
        }

        private static DataLoader CreateDbfAndKmlLoader(string fileSetName, DataLoadingTarget target, FileStream stream, bool sourceOrder, TableOverwriteMode overwriteMode)
        {
            switch (target)
            {
                case DataLoadingTarget.Console:
                    return CreateDbfAndKmlConsoleLoader(stream, fileSetName, sourceOrder);
                case DataLoadingTarget.Tables:
                    return CreateDbfAndKmlTablesLoader(stream, fileSetName, sourceOrder, overwriteMode);
            }
            throw new NotSupportedException(target.ToString());
        }

        private static DataLoader CreateDbfAndKmlTablesLoader(FileStream stream, string fileSetName, bool sourceOrder, TableOverwriteMode overwriteMode)
        {
            var serializer = new XmlSerializer(typeof(DbaseKmlToTablesDataLoaderParams));
            var p = (DbaseKmlToTablesDataLoaderParams)serializer.Deserialize(stream);
            p.TableMetadataEntity.EntityKind = p.TableMetadataEntity.EntitySet + "Item";
            if (p.ProcessorParams != null)
            {
                p.ProcessorParams.EntityKind = p.TableMetadataEntity.EntityKind;
                p.ProcessorParams.EntitySet = p.TableMetadataEntity.EntitySet;
            }
            EntityProducer producer = null;
            if (!p.TableMetadataEntity.IsEmpty)
                producer = new DbaseKmlEntityProducer(
                    fileSetName,
                    p.TableMetadataEntity.EntitySet,
                    p.TableMetadataEntity.EntityKind,
                    p.ProducerParams,
                    sourceOrder);
            var processor = new TableEntityProcessor(p.ProcessorParams, overwriteMode);
            return new TableDataLoader(p, producer, processor, overwriteMode);
        }

        private static DataLoader CreateDbfAndKmlConsoleLoader(FileStream stream, string fileSetName, bool sourceOrder)
        {
            var serializer = new XmlSerializer(typeof(DbaseKmlDataLoaderParams));
            var p = (DbaseKmlDataLoaderParams)serializer.Deserialize(stream);
            p.TableMetadataEntity.EntityKind = p.TableMetadataEntity.EntitySet + "Item";
            EntityProducer producer = null;
            if (!p.TableMetadataEntity.IsEmpty)
                producer = new DbaseKmlEntityProducer(
                    fileSetName,
                    p.TableMetadataEntity.EntitySet,
                    p.TableMetadataEntity.EntityKind,
                    p.ProducerParams,
                    sourceOrder);
            var processor = new CoutEntityProcessor();
            return new DataLoader(p, producer, processor);
        }
    }
}
